//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Convert waypoint or track to object of map.
//

# include "StdAfx.h"
# include "ConvertTrack.h"
# include "UndoRedo.h"
# include "Generalize.h"
# include "GeneralizeRgn.h"
# include "Levels.h"
# include "Label.h"
# include "Types.h"

class CConvertWaypointOrTrackAction : public CEditAction {
  public:
	CConvertWaypointOrTrackAction (const levels_t & _Levels, const rgn_t * _pRgn, RGN_KIND _Kind, bool _bGeneralize) :
		m_Levels (_Levels),
		m_pRgn (_pRgn),
		m_pRgnNew (NULL),
		m_Kind (_Kind),
		m_bGeneralize (_bGeneralize)
	{}

	virtual void GetName (string_t & _str) const;
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	levels_t const m_Levels;
	rgn_t const * const m_pRgn;
	rgn_t * m_pRgnNew;
	const RGN_KIND m_Kind;
	const bool m_bGeneralize;
};

void CConvertWaypointOrTrackAction::GetName (string_t & _str) const {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	if (m_pRgn->IsTrack ())
		_str.append ("convert track [");
	else
		_str.append ("convert waypoint [");
	GetHintText (* m_pRgn, _str);
	_str.append ("]");
}

bool CConvertWaypointOrTrackAction::Do () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_pRgn->IsWTR ());
	assert (! m_pRgnNew);

	// Create the new object.
	const rgns_t::iterator iRgn = g_map.CreateRgnIt (m_Kind);
	m_pRgnNew = & * iRgn;

	// Initialize the object.
	m_pRgnNew->dwFlags   |= rgn_t::fModified;

	m_pRgnNew->strLabel   = m_pRgn->strLabel;

	m_pRgnNew->strComment = m_pRgn->strComment;
	if (m_pRgn->pFileRef) {
		if (! m_pRgnNew->strComment.empty ())
			m_pRgnNew->strComment.append ("\r\n");
		m_pRgnNew->strComment.append ("Imported from ");
		m_pRgnNew->strComment.append (m_pRgn->pFileRef->strFileName);
	}

	if (m_pRgn->IsWaypoint () && m_Kind == rkPoint)
		m_pRgnNew->SetType (m_pRgn->GetTypeDesc (), m_pRgn->GetType (), m_Kind);
	else
		m_pRgnNew->SetType (g_map.pTypeSet->GetUnknownTD (), 0, m_Kind);

	const pfRoundPoint_t  pfRoundPoint  = g_map.pTypeSet->pfRoundPoint;
	const pfRoundPoints_t pfRoundPoints = g_map.pTypeSet->pfRoundPoints;

	// Copy elements.
	const rgn_elements_t & elements = m_pRgn->elements;
	const size_t cElements = elements.size ();
	if (cElements) {
		const size_t cElementsNew = cElements*m_Levels.size ();
		m_pRgnNew->elements.resize (cElementsNew);

		size_t cElementNew = 0;
		for (levels_t::const_iterator il = m_Levels.begin (); il != m_Levels.end (); ++ il) {
			const size_t cLevelIdx = * il;

			const float fLatGridInM = GetGeneralizationThreshold (cLevelIdx);
			const float fGeneralizationStep = __max (fLatGridInM, 3)*360.f/40.e6f;

			for (size_t cElement = 0; cElement < cElements; ++ cElement, ++ cElementNew) {
				const rgn_element_t & element = elements [cElement];

				assert (cElementNew < cElementsNew);
				rgn_element_t & elementNew = m_pRgnNew->elements [cElementNew];

				elementNew.cFileOffset = -1;
				elementNew.cLevel      = cLevelIdx;

				// Copy points.
				elementNew.points = element.points;

				// Generalize them.
				if (m_bGeneralize && fGeneralizationStep)
					Generalize (elementNew.points, fGeneralizationStep, g_map.fCosY);

				// Round them.
				pfRoundPoints (g_map, cLevelIdx, elementNew.points);
				elementNew.RemoveRepeatingPoints ();
			}
		}
	}

	// Copy origins.
	const rgn_origins_t & origins = m_pRgn->origins;
	const size_t cOrigins = origins.size ();
	if (cOrigins) {
		const size_t cOriginsNew = cOrigins*m_Levels.size ();
		m_pRgnNew->origins.resize (cOriginsNew);

		size_t cOriginNew = 0;
		for (levels_t::const_iterator il = m_Levels.begin (); il != m_Levels.end (); ++ il) {
			const size_t cLevelIdx = * il;

			for (size_t cOrigin = 0; cOrigin < cOrigins; ++ cOrigin, ++ cOriginNew) {
				const rgn_origin_t & origin = origins [cOrigin];

				assert (cOriginNew < cOriginsNew);
				rgn_origin_t & originNew = m_pRgnNew->origins [cOriginNew];

				// Initialize the element of rgn.
				originNew.cFileOffset = -1;
				originNew.cLevel      = cLevelIdx;

				// Copy point.
				originNew.point = origin.point;
				// Round it.
				pfRoundPoint (g_map, cLevelIdx, originNew.point);
			}
		}
	}

	m_pRgnNew->RefreshAttributes (g_map);

	// Add new object to the selection.
	find_info_t fi;
	fi.pLayer = & g_map.layers [m_Kind];
	fi.iRgn = iRgn;
	fi.cElementOrOrigin = -1;
	g_Selection.push_back (fi);

	return true;
}

void CConvertWaypointOrTrackAction::Undo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgnNew);
	m_pRgnNew->Delete (g_map);
}

void CConvertWaypointOrTrackAction::Redo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgnNew);
	m_pRgnNew->Undelete (g_map);
}

//////////////////////////////

void ConvertWaypointOrTrack (const levels_t & _Levels, const rgn_t * _pRgn, RGN_KIND _Kind, bool _bGeneralize) {
	assert (_pRgn);
	assert (_pRgn->IsWTR ());
	PerformEditAction (new CConvertWaypointOrTrackAction (_Levels, _pRgn, _Kind, _bGeneralize));
}